package com.demo.util.excel.utils;

import com.demo.util.BeanUtil;
import com.demo.util.CollUtil;
import com.demo.util.excel.beans.ExcelColumnVO;
import com.demo.util.excel.beans.ExcelHeaderVO;
import com.demo.util.tree.utils.TreeUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ExportExcelUtil {

    /**
     * 添加标签页
     */
    public static <T> Sheet addSheet(
            @NotNull XSSFWorkbook workbook,
            @NotNull String sheetName,
            Map<String, XSSFCellStyle> styleMap,
            Function<T, String> rowCellStyle,
            List<ExcelColumnVO<T>> columns,
            List<T> data
    ) {
        // 创建标签页
        Sheet sheet = workbook.createSheet(sheetName);
        // 写入标签页数据
        writeSheet(sheet, styleMap, rowCellStyle, columns, data);
        // 返回创建的标签页
        return sheet;
    }

    /**
     * 写入标签页
     */
    public static <T> void writeSheet(
            @NotNull Sheet sheet,
            Map<String, XSSFCellStyle> styleMap,
            Function<T, String> rowCellStyle,
            List<ExcelColumnVO<T>> columns,
            List<T> data
    ) {
        // 添加标题单元格
        ExcelHeaderVO<T> headerInfo = addSheetHeaders(sheet, styleMap, columns);
        // 添加数据单元格
        int dataStartIndex = addSheetDataList(sheet, styleMap, rowCellStyle, headerInfo.getColumns(), data);
        // 设置自动列宽
        ExcelWidthUtil.setColumnAutoSize(sheet, headerInfo.getLastIndex(), data.size());
        // 合并表头单元格
        ExcelMergeUtil.mergeHeaderCell(sheet, headerInfo);
        // 合并数据单元格
        ExcelMergeUtil.mergeDataCell(sheet, headerInfo.getColumns(), data, dataStartIndex);
    }

    /**
     * 添加标题单元格
     */
    public static <T> ExcelHeaderVO<T> addSheetHeaders(
            @NotNull Sheet sheet,
            Map<String, XSSFCellStyle> styleMap,
            List<ExcelColumnVO<T>> columns
    ) {
        final int headerStartIndex = sheet.getPhysicalNumberOfRows();
        // 构建表头列表
        List<List<ExcelColumnVO<T>>> headerColumns = TreeUtil.getTreePathsList(columns);
        // 渲染表头
        int maxLength = headerColumns.stream().mapToInt(List::size).max().getAsInt();
        for (int rowIndex = 0; rowIndex < maxLength; rowIndex++) {
            Row row = sheet.createRow(sheet.getPhysicalNumberOfRows());
            for(int colIndex = 0; colIndex < headerColumns.size(); colIndex++) {
                List<ExcelColumnVO<T>> rowColumns = headerColumns.get(colIndex);
                ExcelColumnVO<T> column = CollUtil.getOrLast(rowColumns, rowIndex);
                Cell cell = row.createCell(colIndex);
                cell.setCellStyle(ExcelStyleUtil.getHeaderCellStyle(column, styleMap));
                if (column != null) {
                    cell.setCellValue(column.getTitle());
                }
            }
        }
        // 获取数据绑定表头列表
        List<ExcelColumnVO<T>> lastRowColumns = headerColumns
                .stream()
                .map(CollUtil::getLast)
                .collect(Collectors.toList());
        return new ExcelHeaderVO<>(
                headerStartIndex,
                maxLength,
                headerColumns,
                lastRowColumns
        );
    }

    /**
     * 添加数据单元格
     */
    public static <T> int addSheetDataList(
            @NotNull Sheet sheet,
            Map<String, XSSFCellStyle> styleMap,
            Function<T, String> rowCellStyle,
            List<ExcelColumnVO<T>> columns,
            List<T> data
    ) {
        final int dataStartIndex = sheet.getPhysicalNumberOfRows();
        for (T item : data) {
            Row row = sheet.createRow(sheet.getPhysicalNumberOfRows());
            for (int colIndex = 0; colIndex < columns.size(); colIndex++) {
                ExcelColumnVO<T> column = columns.get(colIndex);
                Cell cell = row.createCell(colIndex);
                cell.setCellStyle(ExcelStyleUtil.getDataCellStyle(column, styleMap, rowCellStyle, item));
                ExcelCellValueUtil.setDataCellValue(
                        cell,
                        column.getDataType(),
                        BeanUtil.getFieldValue(item, column.getField())
                );
            }
        }
        return dataStartIndex;
    }

}
